package com.fit.spring.boot.properties.autoscan.autoconfigure;

import com.fit.spring.boot.properties.autoscan.utils.IOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;


/**
 * 扫描properties配置文件,将配置项加入到Environment中
 * PS:
 * 	如果properties的classpath名称('class path resource ["classpath"]')相同时, 覆盖操作。
 *  覆盖操作时,外部项目中的文件优先级高于jar包中classpath相同的文件。
 * @author hcl
 *
 */
public class AutoScanLoadSourceConfiguration implements EnvironmentPostProcessor{

    private static Logger log = LoggerFactory.getLogger(AutoScanLoadSourceConfiguration.class);

    /**
     * @description: main 的class
     */
    private static Class<?> MAIN_CLASS;

    /**
     * @describe: 不是jar
     */
    private static boolean NOT_JAR;


    static{
        //初始化main 的class
		String mainClassName = System.getProperty("sun.java.command");
		log.info("'sun.java.command' value : '{}'", mainClassName);
		System.out.println(String.format("'sun.java.command' value : '%s'",mainClassName));

		if(!mainClassName.contains("junit.JUnitStarter")){
			if(mainClassName.contains(" ")){
				mainClassName = mainClassName.split(" ")[1];
			}
			try {
				MAIN_CLASS = Class.forName(mainClassName);
                NOT_JAR = false;
			} catch (ClassNotFoundException e) {
//				throw new RuntimeException("初始化main的class异常！"+mainClassName);
				MAIN_CLASS = null;
                NOT_JAR = true;
			}
		}else{
			MAIN_CLASS = null;
            NOT_JAR = true;
		}

    }


	/**
	 * 初始化Environment
	 */
	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		String classPath = "";
		try {
            classPath = new ClassPathResource(classPath).getURI().toString();
			log.info("classPath:'{}'",classPath);
            System.out.println(String.format("classPath:'%s'",classPath));
		} catch (IOException e) {
			log.error("读取classpath路径异常！",e);
			throw new RuntimeException("读取classpath路径异常！",e);
		}
		MutablePropertySources mutablePropertySources = environment.getPropertySources();

		//加载auto
        String autoUri = AutoScanPropertiesProperties.getUriByAutoActive();
        this.readFileConfigByUri(autoUri,classPath,mutablePropertySources,true);

        //根据模式加载文件
		List<String> typesUri = AutoScanPropertiesProperties.getAllUri(environment.getActiveProfiles());
		for(String uri : typesUri){
            this.readFileConfigByUri(uri,classPath,mutablePropertySources,true);
		}

		//优先级较高的模式
        String priorityActive = environment.getProperty(AutoScanPropertiesProperties.priorityActiveKey);
        if(priorityActive != null && priorityActive.trim().length() != 0){
            String priorityUri = AutoScanPropertiesProperties.getUriByActive(priorityActive);
            this.readFileConfigByUri(priorityUri,classPath,mutablePropertySources,false);
        }

	}

	/**
	 * @describe: 根据uri读取配置
	 * @author: hcl
	 * @date: 2018/7/19 18:16
	 * @param: [uri, classPath, mutablePropertySources, depositLast] //depositLast 存储配置文件到最后
	 * @return void
	 */
	private void readFileConfigByUri(String uri,String classPath,MutablePropertySources mutablePropertySources,boolean depositLast){
        if(NOT_JAR){
            this.addPropertySource(uri, classPath, mutablePropertySources,depositLast);
        }else{
            String path = MAIN_CLASS.getProtectionDomain().getCodeSource().getLocation().getPath();
            if(path.endsWith(".jar")){
                this.addPropertySourceJar(uri, classPath, mutablePropertySources,path.substring(path.lastIndexOf("/")+1),depositLast);
            }else{
                this.addPropertySource(uri, classPath, mutablePropertySources,depositLast);
            }
        }
    }


	/**
	 * @description:
	 * @param: [location, classPath, mutablePropertySources]
	 * @return: void
	 * @author: hcl
	 * @date: created in 2018/2/25 10:09
	 */
    private void addPropertySourceJar(String location, String classPath, MutablePropertySources mutablePropertySources,String jarName,boolean depositLast){

        if(location == null || location.trim().equals("")){
            return ;
        }
        List<String> names = new ArrayList<String>();

        String[] locationsArr = location.split(",");
        for(String locationsPath : locationsArr){
            PathMatchingResourcePatternResolver configProperties = new PathMatchingResourcePatternResolver();
            Resource[] configSources;
            try {
                configSources = configProperties.getResources(locationsPath.trim());

                Set<Resource> resourceSet = new TreeSet<Resource>(new Comparator<Resource>() {
                    @Override
                    public int compare(Resource resource, Resource resource1) {
                        try {
                            String uri = resource.getURI().toString();
                            String uri1 = resource1.getURI().toString();
                            if(uri.contains(jarName) && uri1.contains(jarName)){
                                return -1;
                            }if(uri.contains(jarName)){
                                return -1;
                            }else{
                                return 1;
                            }
                        } catch (IOException e) {
                            log.error("资源 '{}' 扫描异常！",locationsPath, e);
                            throw new RuntimeException(String.format("资源 '%s' 扫描异常！",locationsPath),e);
                        }
                    }
                });


                for(Resource resource : configSources){
                    resourceSet.add(resource);
                }

                for(Resource resource : resourceSet){
                    String uri = resource.getURI().toString();
                    if(log.isDebugEnabled()){
                        log.debug("加载配置文件：'{}'" , uri);
                    }
//                    //同名情况下是否优先
//                    boolean priority = false;
//                    int classPathIndex = uri.lastIndexOf(classPath);
//                    if(classPathIndex >= 0){
//                        //此处不是jar包中配置,优先级比jar包中配置高,所以此处为true
//                        priority = true;
//                        uri = uri.substring(classPathIndex + classPath.length());
//                    }
//                    uri = uri.substring(uri.lastIndexOf("!")+1);
//                    if(uri.startsWith("/")){
//                        uri = uri.substring(1);
//                    }
                    String name = "class path* resource [" + uri +"]";

                    if(mutablePropertySources.get(name) == null){
                        InputStream in = null;
                        try{
                            if(uri.endsWith(".yml") || uri.endsWith(".yaml")){
                                YamlPropertySourceLoader yamlSource = new YamlPropertySourceLoader();
                                if(depositLast){
                                    mutablePropertySources.addLast(yamlSource.load(name, resource, null));
                                }else{
                                    mutablePropertySources.addFirst(yamlSource.load(name, resource, null));
                                }
                            }else if(uri.endsWith(".properties")){
                                in = resource.getInputStream();
                                Properties pro = new Properties();
                                pro.load(in);
                                PropertiesPropertySource propertySource = new PropertiesPropertySource(name, pro);
                                if(depositLast){
                                    mutablePropertySources.addLast(propertySource);
                                }else{
                                    mutablePropertySources.addFirst(propertySource);
                                }
                            }else{
                                throw new RuntimeException(String.format("此文件'%s'后缀不支持加载!",uri));
                            }
                            log.debug("加载 '{}' 配置文件成功!" , name);
                            names.add(name);
                        }catch (Exception e) {
                            log.error("加载 '{}' 配置文件异常!",name, e);
                            throw new RuntimeException(String.format("加载 '%s' 配置文件异常!",name));
                        }finally{
                            IOUtil.close(in);
                        }
                    }else{
                        log.warn("加载 '{}' 配置文件失败!重复加载。", name);
                    }
                }
            } catch (IOException e) {
                log.error("资源 '{}' 扫描异常！",locationsPath, e);
                throw new RuntimeException(String.format("资源 '%s' 扫描异常！",locationsPath), e);
            }
        }
        log.warn("成功加载配置文件：");
        System.out.println("成功加载配置文件：");
        for(String name : names){
            log.warn(name);
            System.out.println(name);
        }

    }

    /**
	 * 根据路径添加配置文件
	 * @param location
	 * @param classPath
	 * @param mutablePropertySources
	 */
	private void addPropertySource(String location, String classPath, MutablePropertySources mutablePropertySources,boolean depositLast){


		if(location == null || location.trim().equals("")){
			return ;
		}
		List<String> names = new ArrayList<String>();

		String[] locationsArr = location.split(",");
		for(String locationsPath : locationsArr){
			PathMatchingResourcePatternResolver configProperties = new PathMatchingResourcePatternResolver();
			Resource[] configSources;
			try {
				configSources = configProperties.getResources(locationsPath.trim());
				for(Resource resource : configSources){
					String uri = resource.getURI().toString();
					if(log.isDebugEnabled()){
						log.debug("开始加载配置文件：'{}'" , uri);
					}
					System.out.println(String.format("开始加载配置文件：'%s'",uri));
					//同名情况下是否优先
					boolean priority = false;
					int classPathIndex = uri.lastIndexOf(classPath);
					if(classPathIndex >= 0){
						//此处不是jar包中配置,优先级比jar包中配置高,所以此处为true
						priority = true;
						uri = uri.substring(classPathIndex + classPath.length());
					}
					uri = uri.substring(uri.lastIndexOf("!")+1);
					if(uri.startsWith("/")){
						uri = uri.substring(1);
					}
					String name = "class path"+( priority ? "" : "*" )+" resource [" + uri +"]";
					
					if(mutablePropertySources.get(name) == null){
						InputStream in = null;
						try{
							if(uri.endsWith(".yml") || uri.endsWith(".yaml")){
								YamlPropertySourceLoader yamlSource = new YamlPropertySourceLoader();
                                if(depositLast){
                                    mutablePropertySources.addLast(yamlSource.load(name, resource, null));
                                }else{
                                    mutablePropertySources.addFirst(yamlSource.load(name, resource, null));
                                }

							}else if(uri.endsWith(".properties")){
								in = resource.getInputStream();
								Properties pro = new Properties();
								pro.load(in);
								PropertiesPropertySource propertySource = new PropertiesPropertySource(name, pro);
                                if(depositLast){
                                    mutablePropertySources.addLast(propertySource);
                                }else{
                                    mutablePropertySources.addFirst(propertySource);
                                }
							}else{
								throw new RuntimeException(String.format("此文件'%s'后缀不支持加载!", uri));
							}
							log.debug("加载 '{}' 配置文件成功!" , name);
							names.add(name);
						}catch (Exception e) {
							log.error("加载 '{}' 配置文件异常!",name, e);
							throw new RuntimeException(String.format("加载 '%s' 配置文件异常!",name),e);
						}finally{
							IOUtil.close(in);
						}
					}else{
						log.warn("加载 '{}' 配置文件失败!重复加载。", name);
					}
				}
			} catch (IOException e) {
				log.error("资源 '{}' 扫描异常！",locationsPath, e);
				throw new RuntimeException("资源 '" + locationsPath + "' 扫描异常！", e);
			}
		}
		log.warn("成功加载配置文件：");
		System.out.println("成功加载配置文件：");
		for(String name : names){
			log.warn(name);
			System.out.println(name);
		}
	}
}
